/**
 * @file SweepDialog.cpp
 * @author  Johan Waldeback <openfms@waldeback.se>
 *
 * @section LICENSE
 *
 * Copyright (C) 2010 Johan Waldeback
 *
 * This file is part of the OpenFMS project (http://www.openfms-project.org)
 * 
 * OpenFMS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @section DESCRIPTION
 *
 * The time class represents a moment of time.
 */

#include "SweepDialog.h"

#include <wx/wx.h>
#include <cassert>
#include <sstream>

using namespace OpenFms;

#ifdef MAKE_TEST
#define testable_static
#else
#define testable_static static
#endif


// TODO: REMOVE THIS !!!!
float last_interpolate ;

/**
 * assert specified id is an edit control id
 */
#define assertIdIsTextCtrl( idEdit ) assert(  \
		idEdit == ID_WINDX_VALUE || idEdit ==  ID_WINDX_BEGIN || idEdit ==  ID_WINDX_END ||   \
		idEdit == ID_WINDY_VALUE || idEdit ==  ID_WINDY_BEGIN || idEdit ==  ID_WINDY_END ||   \
		idEdit == ID_WINDZ_VALUE || idEdit ==  ID_WINDZ_BEGIN || idEdit == ID_WINDZ_END  ||   \
		idEdit == ID_WINDYAW_VALUE   || idEdit == ID_WINDYAW_BEGIN   || idEdit == ID_WINDYAW_END   ||  \
		idEdit == ID_WINDPITCH_VALUE || idEdit == ID_WINDPITCH_BEGIN || idEdit == ID_WINDPITCH_END || \
		idEdit == ID_CHANNEL1_VALUE || idEdit == ID_CHANNEL1_BEGIN || idEdit == ID_CHANNEL1_END ||  \
		idEdit == ID_CHANNEL2_VALUE || idEdit == ID_CHANNEL2_BEGIN || idEdit == ID_CHANNEL2_END ||  \
		idEdit == ID_CHANNEL3_VALUE || idEdit == ID_CHANNEL3_BEGIN || idEdit == ID_CHANNEL3_END ||  \
		idEdit == ID_CHANNEL4_VALUE || idEdit == ID_CHANNEL4_BEGIN || idEdit == ID_CHANNEL4_END ||  \
		idEdit == ID_CHANNEL5_VALUE || idEdit == ID_CHANNEL5_BEGIN || idEdit == ID_CHANNEL5_END ||  \
		idEdit == ID_CHANNEL6_VALUE || idEdit == ID_CHANNEL6_BEGIN || idEdit == ID_CHANNEL6_END ||  \
		idEdit == ID_CHANNEL7_VALUE || idEdit == ID_CHANNEL7_BEGIN || idEdit == ID_CHANNEL7_END ||  \
		idEdit == ID_CHANNEL8_VALUE || idEdit == ID_CHANNEL8_BEGIN || idEdit == ID_CHANNEL8_END || idEdit == ID_STEPS)

/**
 * assert specified id is a valid base id.
 */
#define assertIdIsBase( idBase ) assert( \
		idBase == ID_WINDX  || idBase == ID_WINDY  || idBase == ID_WINDZ  || idBase == ID_WINDYAW  || idBase == ID_WINDPITCH ||	\
		idBase == ID_CHANNEL1 || idBase == ID_CHANNEL2 || idBase == ID_CHANNEL3 || idBase == ID_CHANNEL4 || \
		idBase == ID_CHANNEL5 || idBase == ID_CHANNEL6 || idBase == ID_CHANNEL7 || idBase == ID_CHANNEL8 )

/**
 * assert specified id is a valid output radio button control
 */
#define assertIdIsOutput( idForce ) assert ( \
		idForce == ID_FORCE_POSX || idForce == ID_FORCE_POSY || idForce == ID_FORCE_POSZ    || \
		idForce == ID_FORCE_NEGX || idForce == ID_FORCE_NEGY || idForce == ID_FORCE_NEGZ    || \
		idForce == ID_TORQUE_POSX || idForce == ID_TORQUE_POSY || idForce == ID_TORQUE_POSZ || \
		idForce == ID_TORQUE_NEGX || idForce == ID_TORQUE_NEGY || idForce == ID_TORQUE_NEGZ  )

#define ID_VALUE(idBase) (idBase+1)
#define ID_BEGIN(idBase) (idBase+2)
#define ID_END(idBase) (idBase+3)

// defines used by addRow(..)
#define TYPE_MS 1
#define TYPE_RAD 2
#define TYPE_CTRL 4
#define TYPE_FIRST 8

/**
 * Helper function - adds a row of controls to the gien size
 *
 * Will add a row of controls to the specified sizer:
 *  1. (leftmost) a static text, specified by the title parameter
 *  2. Value edit control with id = idBase+1
 *  3. Radio button control with id = idBase 
 *  4. Range begin edit control with id = idBase+2
 *  5. Range end edit control with id = idBase+3
 *  6. (rightmost) Static text containing the unit of the values in the edit controls
 *
 * One, and only one, of the types TYPE_MS, TYPE_RAD, TYPE_CTRL should be specified as type
 * TYPE_FIRST should be ored to the type in the first, and only the first, row.
 *
 * All value fileds will be set to zero by default.
 * Using TYPE_MS (indicating wind speed in m/s) - Range is set to [0,1]
 * Using TYPE_RAD (indicating angle in radians) - Range is set to [-3.14, 3,14]
 * Using TYPE_CTRL (indicating input control) - Range is set to [-1,1]
 * 
 * @param sizer Pointer to the sizer used
 * @param parent Pointer to the windows which should become the parent to all of tha added controls
 * @param title Static text put to the left of the row
 * @param idBase Base id of the controls to be adden
 * @param type Type if data to be entered in the row
 */
static void addRow( wxSizer *sizer, wxWindow *parent, const wxString &title, int idBase, int type )
{
	// TODO: add validators?

	// Note: 
	//  Radio button ID = base ID
	//  Value Edit ID   = base ID +1
	//  Begin Edit ID   = base DI +2
	//  End Edit ID     = base ID +3

	sizer->Add( new wxStaticText(parent, wxID_ANY, title));
	sizer->Add( new wxTextCtrl(parent, idBase+1, wxT("0")),1);

	// add radio button, set ti first if first flag is specified
	sizer->Add( new wxRadioButton( parent, idBase, wxT(""), wxDefaultPosition, wxDefaultSize, type & TYPE_FIRST ? wxRB_GROUP : 0 ));

	if( type & TYPE_MS ) {
		sizer->Add( new wxTextCtrl(parent, idBase+2, wxT("0")),1);
		sizer->Add( new wxTextCtrl(parent, idBase+3, wxT("1.0")),1);
		sizer->Add( new wxStaticText(parent, wxID_ANY, wxT("m/s")));
	}
	else if( type & TYPE_RAD ) {
		sizer->Add( new wxTextCtrl(parent, idBase+2, wxT("-3.14")));
		sizer->Add( new wxTextCtrl(parent, idBase+3, wxT("3.14")));
		sizer->Add( new wxStaticText(parent, wxID_ANY, wxT("rad")));
	}
	else //if( type & TYPE_CTRL ) 
	{
		sizer->Add( new wxTextCtrl(parent, idBase+2, wxT("-1.00")));
		sizer->Add( new wxTextCtrl(parent, idBase+3, wxT("1.00")));
		sizer->Add( new wxStaticText(parent, wxID_ANY, wxT("-")));
	}
}

/**
 * SweepDialog contructor
 *
 * @param parent Pointer to parent window. Set to NULL if top window.
 * @param id Id to be assigned to the window, set to wxID_ANY if don't care.
 * @param title Window title.
 */
SweepDialog::SweepDialog(wxWindow *parent, wxWindowID id, const wxString &title) : 
	wxDialog(parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/*|wxRESIZE_BORDER*/ )
{
	m_isValidData = false;

	wxPanel *panel = new wxPanel( this );
	Connect( ID_OKBUTTON, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SweepDialog::OnOk) );
	Connect( ID_CANCELBUTTON, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SweepDialog::OnCancel) );

	wxFlexGridSizer *ingrid = new wxFlexGridSizer( 6, 5, 5 );
	wxStaticBoxSizer *inbox = new wxStaticBoxSizer(new wxStaticBox( panel, wxID_ANY, wxT("Sweep variable")), wxVERTICAL );
	inbox->Add( ingrid );

	addRow( ingrid, panel, wxT("Wind X:"),     ID_WINDX,     TYPE_MS | TYPE_FIRST );
	addRow( ingrid, panel, wxT("Wind Y:"),     ID_WINDY,     TYPE_MS );
	addRow( ingrid, panel, wxT("Wind Z:"),     ID_WINDZ,     TYPE_MS );
	addRow( ingrid, panel, wxT("Wind Yaw:"),   ID_WINDYAW,   TYPE_RAD );
	addRow( ingrid, panel, wxT("Wind Pitch:"), ID_WINDPITCH, TYPE_RAD );
	addRow( ingrid, panel, wxT("Channel 1:"),  ID_CHANNEL1,  TYPE_CTRL );
	addRow( ingrid, panel, wxT("Channel 2:"),  ID_CHANNEL2,  TYPE_CTRL );
	addRow( ingrid, panel, wxT("Channel 3:"),  ID_CHANNEL3,  TYPE_CTRL );
	addRow( ingrid, panel, wxT("Channel 4:"),  ID_CHANNEL4,  TYPE_CTRL );
	addRow( ingrid, panel, wxT("Channel 5:"),  ID_CHANNEL5,  TYPE_CTRL );
	addRow( ingrid, panel, wxT("Channel 6:"),  ID_CHANNEL6,  TYPE_CTRL );
	addRow( ingrid, panel, wxT("Channel 7:"),  ID_CHANNEL7,  TYPE_CTRL );
	addRow( ingrid, panel, wxT("Channel 8:"),  ID_CHANNEL8,  TYPE_CTRL );

	wxGridSizer *outgrid = new wxGridSizer( 6, 5, 5 );
	wxStaticBoxSizer *outbox = new wxStaticBoxSizer( new wxStaticBox( panel, wxID_ANY, wxT("Output variable")), wxVERTICAL );
	outbox->Add( outgrid );
	outgrid->Add( new wxRadioButton( panel, ID_FORCE_POSX, wxT("Force X"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP));
	outgrid->Add( new wxRadioButton( panel, ID_FORCE_POSY, wxT("Force Y")));
	outgrid->Add( new wxRadioButton( panel, ID_FORCE_POSZ, wxT("Force Z")));
	outgrid->Add( new wxRadioButton( panel, ID_TORQUE_POSX, wxT("Torque X")));
	outgrid->Add( new wxRadioButton( panel, ID_TORQUE_POSY, wxT("Torque Y")));
	outgrid->Add( new wxRadioButton( panel, ID_TORQUE_POSZ, wxT("Torque Z")));
	outgrid->Add( new wxRadioButton( panel, ID_FORCE_NEGX, wxT("Force -X")));
	outgrid->Add( new wxRadioButton( panel, ID_FORCE_NEGY, wxT("Force -Y")));
	outgrid->Add( new wxRadioButton( panel, ID_FORCE_NEGZ, wxT("Force -Z")));
	outgrid->Add( new wxRadioButton( panel, ID_TORQUE_NEGX, wxT("Torque -X")));
	outgrid->Add( new wxRadioButton( panel, ID_TORQUE_NEGY, wxT("Torque -Y")));
	outgrid->Add( new wxRadioButton( panel, ID_TORQUE_NEGZ, wxT("Torque -Z")));

	wxBoxSizer *okbox = new wxBoxSizer(wxHORIZONTAL);
	okbox->Add( new wxStaticText( panel, wxID_ANY, wxT("Number of points:")));
	okbox->Add( new wxTextCtrl( panel, ID_STEPS, wxT("101") ));
	okbox->AddStretchSpacer();
	okbox->Add( new wxButton( panel, ID_OKBUTTON, "&Run"), 0, wxRIGHT|wxLEFT|wxTOP|wxBOTTOM, 5);
	okbox->Add( new wxButton( panel, ID_CANCELBUTTON, "&Cancel"), 0, wxRIGHT|wxLEFT|wxTOP|wxBOTTOM, 5);

	wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
	vbox->Add(inbox);
	vbox->Add(outbox);
	vbox->Add(okbox, 0, wxALIGN_RIGHT);

	panel->SetSizer(vbox);
	vbox->Fit(this);
}

/**
 * OK button pressed handler
 *
 * Parses all fields and transfers the values to instance variables.
 * Closes the dialog with return code wxID_OK on succes.
 * On fail (e.g. a parsing failure) displays a message box.
 * On fail, the dialog will not get closed
 *
 * @param event wxWidgets event variable - not used.
 */
void SweepDialog::OnOk(wxCommandEvent &event)
{
	// try to transfer data in dialog to instanve environment and inertias varialbe
	// end the dialog with an OK if success
	if( this->transfer() )
	{
		EndDialog(wxID_OK);
	}
	else
	{
		wxMessageBox(wxT("An error ocurred"));
	}
}

/**
 * Cancel button pressed handler
 *
 * Closes the dialog with return code wxID_CANCEL.
 * Note: The instance variables after close are undefined.
 *
 * @param event wxWidgets event variable - not used.
 */
void SweepDialog::OnCancel(wxCommandEvent &event)
{
	EndModal(wxID_CANCEL);
}

#define CHECK_AND_RETURN_IF_SELECTED( ID_X ) {\
	wxRadioButton *button = static_cast<wxRadioButton *>(this->FindWindowById( ID_X, this )); \
	if( button && button->GetValue() ) return ID_X; } \

/**
 * Reads which input (sweep) variable is selected from dialog radio button
 *
 * @return Returns a base id indicating to the fields selected
 */
int SweepDialog::parseSelectedInput()
{
	CHECK_AND_RETURN_IF_SELECTED( ID_WINDX )
	CHECK_AND_RETURN_IF_SELECTED( ID_WINDY )
	CHECK_AND_RETURN_IF_SELECTED( ID_WINDZ )
	CHECK_AND_RETURN_IF_SELECTED( ID_WINDYAW )
	CHECK_AND_RETURN_IF_SELECTED( ID_WINDPITCH )
	CHECK_AND_RETURN_IF_SELECTED( ID_CHANNEL1 )
	CHECK_AND_RETURN_IF_SELECTED( ID_CHANNEL2 )
	CHECK_AND_RETURN_IF_SELECTED( ID_CHANNEL3 )
	CHECK_AND_RETURN_IF_SELECTED( ID_CHANNEL4 )
	CHECK_AND_RETURN_IF_SELECTED( ID_CHANNEL5 )
	CHECK_AND_RETURN_IF_SELECTED( ID_CHANNEL6 )
	CHECK_AND_RETURN_IF_SELECTED( ID_CHANNEL7 )
	CHECK_AND_RETURN_IF_SELECTED( ID_CHANNEL8 )
	assert( false && "Should never end up here, one of the above should always catch the selected input");
	return 0;
}

/**
 * Reads which output variable is selected from dialog radio button
 *
 * @return Returns an output id idicating the choise
 */
int SweepDialog::parseSelectedOutput()
{
	CHECK_AND_RETURN_IF_SELECTED( ID_FORCE_POSX )
	CHECK_AND_RETURN_IF_SELECTED( ID_FORCE_POSY )
	CHECK_AND_RETURN_IF_SELECTED( ID_FORCE_POSZ )
	CHECK_AND_RETURN_IF_SELECTED( ID_FORCE_NEGX )
	CHECK_AND_RETURN_IF_SELECTED( ID_FORCE_NEGY )
	CHECK_AND_RETURN_IF_SELECTED( ID_FORCE_NEGZ )
	CHECK_AND_RETURN_IF_SELECTED( ID_TORQUE_POSX )
	CHECK_AND_RETURN_IF_SELECTED( ID_TORQUE_POSY )
	CHECK_AND_RETURN_IF_SELECTED( ID_TORQUE_POSZ )
	CHECK_AND_RETURN_IF_SELECTED( ID_TORQUE_NEGX )
	CHECK_AND_RETURN_IF_SELECTED( ID_TORQUE_NEGY )
	CHECK_AND_RETURN_IF_SELECTED( ID_TORQUE_NEGZ )
	assert( false && "Should never end up here, one of the above should always catch the selected output");
	return 0;
}

/**
 * Converts a TCHAR string to a float value
 *
 * @param[out] result The result of the parsing, valid only if the function returns true.
 * @param[in] str The string to be parsed. Should be in format [+-]?[digit]*(\.[digit]*)?
 * @return Retruns true on success, false on fail.
 */
testable_static bool parseFloatString( float &result,  const TCHAR *str )
{
	bool negative = false;
	int decimals = -1;
	result = 0.f;

	size_t length = _tcslen(str);
	size_t index = 0;

	if( length >0 ) {
		if( str[0] == _T('+') ) ++index;
		else if( str[0] == _T('-')) {
			++index; negative = true;
		}
	}
	for(; index<length; ++index) {
		if( str[index] == _T('.') || str[index] == _T(',')) {
			if( decimals >= 0 ) {
				// if here, then more than one decimal point
				return false;
			}
			++decimals;
		}
		else 
		{
			if( decimals >= 0 ) ++ decimals;
			if( str[index] <_T('0')  || str[index] > _T('9') ) {
				// if here, then invali char
				return false;
			}
			result = result*10 + static_cast<int>(str[index]-_T('0'));
		}
	}

	for(; decimals >0; --decimals ) result *= 0.1f;
	if(negative) result = -result;
	return true; 
}

/**
 * Return a human-readable name for a specified edit box
 *
 * The returned string can, for example, be used to indicate
 * a specific field to the user, e.g. when he/she has entered 
 * a value in an illegal format.
 *
 * @param idEdit ID to the edit box
 * @return A pointer to a human-readable string
 */
const TCHAR *SweepDialog::getEditName( int idEdit )
{
	assertIdIsTextCtrl( idEdit );

	if( idEdit == ID_WINDX_VALUE ) return _T("ID_WINDX_VALUE");
	if( idEdit == ID_WINDX_BEGIN ) return _T("ID_WINDX_BEGIN");
	if( idEdit == ID_WINDX_END )   return _T("ID_WINDX_END");
	
	if( idEdit == ID_WINDY_VALUE ) return _T("ID_WINDY_VALUE");
	if( idEdit == ID_WINDY_BEGIN ) return _T("ID_WINDY_BEGIN");
	if( idEdit == ID_WINDY_END )   return _T("ID_WINDY_END");
	
	if( idEdit == ID_WINDZ_VALUE ) return _T("ID_WINDZ_VALUE");
	if( idEdit == ID_WINDZ_BEGIN ) return _T("ID_WINDZ_BEGIN");
	if( idEdit == ID_WINDZ_END )   return _T("ID_WINDZ_END");

	if( idEdit == ID_WINDYAW_VALUE ) return _T("ID_WINDYAW_VALUE");
	if( idEdit == ID_WINDYAW_BEGIN ) return _T("ID_WINDYAW_BEGIN");
	if( idEdit == ID_WINDYAW_END )   return _T("ID_WINDYAW_END");

	if( idEdit == ID_WINDPITCH_VALUE ) return _T("ID_WINDPITCH_VALUE");
	if( idEdit == ID_WINDPITCH_BEGIN ) return _T("ID_WINDPITCH_BEGIN");
	if( idEdit == ID_WINDPITCH_END )   return _T("ID_WINDPITCH_END");

	if( idEdit == ID_CHANNEL1_VALUE ) return _T("ID_CHANNEL1_VALUE");
	if( idEdit == ID_CHANNEL1_BEGIN ) return _T("ID_CHANNEL1_BEGIN");
	if( idEdit == ID_CHANNEL1_END )   return _T("ID_CHANNEL1_END");
	if( idEdit == ID_CHANNEL2_VALUE ) return _T("ID_CHANNEL2_VALUE");
	if( idEdit == ID_CHANNEL2_BEGIN ) return _T("ID_CHANNEL2_BEGIN");
	if( idEdit == ID_CHANNEL2_END )   return _T("ID_CHANNEL2_END");
	if( idEdit == ID_CHANNEL3_VALUE ) return _T("ID_CHANNEL3_VALUE");
	if( idEdit == ID_CHANNEL3_BEGIN ) return _T("ID_CHANNEL3_BEGIN");
	if( idEdit == ID_CHANNEL3_END )   return _T("ID_CHANNEL3_END");
	if( idEdit == ID_CHANNEL4_VALUE ) return _T("ID_CHANNEL4_VALUE");
	if( idEdit == ID_CHANNEL4_BEGIN ) return _T("ID_CHANNEL4_BEGIN");
	if( idEdit == ID_CHANNEL4_END )   return _T("ID_CHANNEL4_END");
	if( idEdit == ID_CHANNEL5_VALUE ) return _T("ID_CHANNEL5_VALUE");
	if( idEdit == ID_CHANNEL5_BEGIN ) return _T("ID_CHANNEL5_BEGIN");
	if( idEdit == ID_CHANNEL5_END )   return _T("ID_CHANNEL5_END");
	if( idEdit == ID_CHANNEL6_VALUE ) return _T("ID_CHANNEL6_VALUE");
	if( idEdit == ID_CHANNEL6_BEGIN ) return _T("ID_CHANNEL6_BEGIN");
	if( idEdit == ID_CHANNEL6_END )   return _T("ID_CHANNEL6_END");
	if( idEdit == ID_CHANNEL7_VALUE ) return _T("ID_CHANNEL7_VALUE");
	if( idEdit == ID_CHANNEL7_BEGIN ) return _T("ID_CHANNEL7_BEGIN");
	if( idEdit == ID_CHANNEL7_END )   return _T("ID_CHANNEL7_END");
	if( idEdit == ID_CHANNEL8_VALUE ) return _T("ID_CHANNEL8_VALUE");
	if( idEdit == ID_CHANNEL8_BEGIN ) return _T("ID_CHANNEL8_BEGIN");
	if( idEdit == ID_CHANNEL8_END )   return _T("ID_CHANNEL8_END");

	if( idEdit == ID_STEPS)   return _T("ID_STEP");

	assert( false && "Should never end up here, should always find the value above");
	return _T("ERROR: Unknown field");
}

/**
 * Parse a float value from a field specified by an id.
 *
 * @param[out] result The result of the parsing, valid only if the function returns true.
 * @param[in] idEdit of the field to be parsed.
 * @return Retruns true on success, false on fail.
 */
bool SweepDialog::parseFloat( float &result, int idEdit )
{
	assertIdIsTextCtrl( idEdit );

	wxTextCtrl *ctrl = static_cast <wxTextCtrl *>(this->FindWindowById( idEdit, this));
	assert( ctrl && "A windows should always be found");
	if( ctrl )
	{
		if( parseFloatString( result, ctrl->GetValue() ))
		{
			//TCHAR buffer[100];
			//_stprintf(buffer, _T(" f = %f") , f);
			//wxMessageBox(buffer, wxT("value"));
			return true;
		}
		else
		{
			TCHAR buffer[100];
			_stprintf(buffer, _T("%s - INVALID VALUE"), this->getEditName(idEdit) );
			wxMessageBox(ctrl->GetValue(), buffer);
		}
	}
	return false;
}

/**
 * Parse an integer value from the number of steps field.
 *
 * The format shoud be [:digit:]*
 *
 * @param[out] result The result of the parsing, valid only if the function returns true.
 * @return Retruns true on success, false on fail.
 */
bool SweepDialog::parseSteps( int &result )
{
	wxTextCtrl *ctrl = static_cast<wxTextCtrl *>(this->FindWindowById( ID_STEPS, this));
	assert( ctrl != NULL && "Should always find the control");
	if(!ctrl) return false;

	wxString text = ctrl->GetValue();
	size_t length = text.Length();
	result = 0;
	for( size_t i =0; i<length; ++i ) {
		if( text[i] < _T('0') || text[i] > _T('9') ) {
			TCHAR buffer[100];
			_stprintf(buffer, _T("%s - INVALID VALUE"), this->getEditName(ID_STEPS) );
			wxMessageBox(ctrl->GetValue(), buffer);
			return false;
		}
		result = result * 10 + static_cast<int>(text[i]-_T('0'));
	}
	return true;
}

#define transferTriplet( id, var_value, var_begin, var_end ) if( \
	!this->parseFloat( var_value, ID_VALUE(id) ) || \
	!this->parseFloat( var_begin, ID_BEGIN(id)) || \
	!this->parseFloat( var_end, ID_END(id)) ) return false;

/**
 *  Transfer data in fields to instance variables
 *
 *  Parse and transfer all editboxes, and read the states of all
 *  radio buttones, etc. And transfer the result to to instance variables.
 *  If the function fails, the instance variables are undefined.
 *
 *  @return Returns true on success, false on fail (e.g. a parsing error)
 */
bool SweepDialog::transfer()
{
	// begin by indicating invalid data. It the trasfer becomes aborted
	// (becouse of a parsing error) this will stick.
	m_isValidData = false;

	// transfer environment fiels
	// note: the transferTriplet macro will exit this function, returning false, 
	//       if any of the paring failes.
	transferTriplet( ID_WINDX, m_wind_x_value, m_wind_x_begin, m_wind_x_end);
	transferTriplet( ID_WINDY, m_wind_y_value, m_wind_y_begin, m_wind_y_end);
	transferTriplet( ID_WINDZ, m_wind_z_value, m_wind_z_begin, m_wind_z_end);
	transferTriplet( ID_WINDYAW,   m_wind_yaw_value,   m_wind_yaw_begin,   m_wind_yaw_end);
	transferTriplet( ID_WINDPITCH, m_wind_pitch_value, m_wind_pitch_begin, m_wind_pitch_end);
	transferTriplet( ID_CHANNEL1,  m_channel1_value, m_channel1_begin, m_channel1_end);
	transferTriplet( ID_CHANNEL2,  m_channel2_value, m_channel2_begin, m_channel2_end);
	transferTriplet( ID_CHANNEL3,  m_channel3_value, m_channel3_begin, m_channel3_end);
	transferTriplet( ID_CHANNEL4,  m_channel4_value, m_channel4_begin, m_channel4_end);
	transferTriplet( ID_CHANNEL5,  m_channel5_value, m_channel5_begin, m_channel5_end);
	transferTriplet( ID_CHANNEL6,  m_channel6_value, m_channel6_begin, m_channel6_end);
	transferTriplet( ID_CHANNEL7,  m_channel7_value, m_channel7_begin, m_channel7_end);
	transferTriplet( ID_CHANNEL8,  m_channel8_value, m_channel8_begin, m_channel8_end);

	// fill in inertias
	// for now, these are just filled in with anything non-zero
	// TODO: add these as fields in the dialog
	m_mass = 1.f;
	m_ixx = 1.0f;
	m_iyy = 1.0f;
	m_izz = 1.0f;
	m_ixy = 1.0f;
	m_ixz = 1.0f;
	m_iyz = 1.0f;
	
	// read the number of steps box, and the input and output radio buttons
	if( this->parseSteps(m_steps) == false ) return false;
	m_chosen_input = this->parseSelectedInput();
	m_chosen_output = this->parseSelectedOutput();

	// If we made it all the way down here, all data variables are set and valid.
	m_isValidData = true;
	return true;
}

/**
 * Get the number of steps/sample points
 *
 * Note: This function will only return a valid value if the transfer() function
 * has run successfully. The intended usage is to call this function after
 * the user has closed the dialog with the OK button to retrive the number 
 * of the requested number of steps/sample points.
 *
 * @return Return the instance variable
 */
int SweepDialog::getNumberOfSteps()
{
	return m_steps;
}

/**
 * Set the number of steps/sample points
 *
 * This function is made public if MAKE_TEST is defined
 *
 * @return Return the instance variable
 */
void SweepDialog::setNumberOfSteps(int n)
{
	m_steps = n;
}

/**
 * Sets the selected input
 *
 * This function is made public if MAKE_TEST is defined
 *
 */
void SweepDialog::setSelectedInput( int id )
{
	assertIdIsBase(id);
	this->m_chosen_input = id;
}

/**
 * Sets the selected output
 *
 * This function is made public if MAKE_TEST is defined
 *
 */
void SweepDialog::setSelectedOutput( int id )
{
	assertIdIsOutput(id);
	this->m_chosen_output = id;
}

int SweepDialog::getSelectedOutput()
{
	return this->m_chosen_output;
}

int SweepDialog::getSelectedInput()
{
	return this->m_chosen_input;
}

/**
 * Sets instance variables
 *
 * This funtion is made public id MAKE_TEST is defined
 * 
 * @param idBase Base id of the instance values to be set
 * @param value Set the VALUE part to this valus
 * @param begin Set the BEGIN part to this valus
 * @param end Set the END part to this valus
 */
void SweepDialog::setValues( int idBase, float value, float begin, float end )
{
	assertIdIsBase(idBase);
	setValue( ID_VALUE(idBase), value );
	setValue( ID_BEGIN(idBase), begin );
	setValue( ID_END(idBase), end );
}

/**
 * Set the instance variable if the corresponding edit box
 *
 * This funtion is made public id MAKE_TEST is defined
 * 
 * @param id Id of the edit box, whose corresponding instance variable is to be set
 * @param value Value to et the instance variable to
 */
void SweepDialog::setValue( int id, float value )
{
	assertIdIsTextCtrl(id);
	switch( id )
	{
	case ID_WINDX_VALUE: this->m_wind_x_value=value; return;
	case ID_WINDX_BEGIN: this->m_wind_x_begin=value; return;
	case ID_WINDX_END:   this->m_wind_x_end=value; return;
	case ID_WINDY_VALUE: this->m_wind_y_value=value; return;
	case ID_WINDY_BEGIN: this->m_wind_y_begin=value; return;
	case ID_WINDY_END:   this->m_wind_y_end=value; return;
	case ID_WINDZ_VALUE: this->m_wind_z_value=value; return;
	case ID_WINDZ_BEGIN: this->m_wind_z_begin=value; return;
	case ID_WINDZ_END:   this->m_wind_z_end=value; return;
	case ID_WINDYAW_VALUE: this->m_wind_yaw_value=value; return;
	case ID_WINDYAW_BEGIN: this->m_wind_yaw_begin=value; return;
	case ID_WINDYAW_END:   this->m_wind_yaw_end=value; return;
	case ID_WINDPITCH_VALUE: this->m_wind_pitch_value=value; return;
	case ID_WINDPITCH_BEGIN: this->m_wind_pitch_begin=value; return;
	case ID_WINDPITCH_END:   this->m_wind_pitch_end=value; return;
	case ID_CHANNEL1_VALUE: this->m_channel1_value=value; return;
	case ID_CHANNEL1_BEGIN: this->m_channel1_begin=value; return;
	case ID_CHANNEL1_END:   this->m_channel1_end=value; return;
	case ID_CHANNEL2_VALUE: this->m_channel2_value=value; return;
	case ID_CHANNEL2_BEGIN: this->m_channel2_begin=value; return;
	case ID_CHANNEL2_END:   this->m_channel2_end=value; return;
	case ID_CHANNEL3_VALUE: this->m_channel3_value=value; return;
	case ID_CHANNEL3_BEGIN: this->m_channel3_begin=value; return;
	case ID_CHANNEL3_END:   this->m_channel3_end=value; return;
	case ID_CHANNEL4_VALUE: this->m_channel4_value=value; return;
	case ID_CHANNEL4_BEGIN: this->m_channel4_begin=value; return;
	case ID_CHANNEL4_END:   this->m_channel4_end=value; return;
	case ID_CHANNEL5_VALUE: this->m_channel5_value=value; return;
	case ID_CHANNEL5_BEGIN: this->m_channel5_begin=value; return;
	case ID_CHANNEL5_END:   this->m_channel5_end=value; return;
	case ID_CHANNEL6_VALUE: this->m_channel6_value=value; return;
	case ID_CHANNEL6_BEGIN: this->m_channel6_begin=value; return;
	case ID_CHANNEL6_END:   this->m_channel6_end=value; return;
	case ID_CHANNEL7_VALUE: this->m_channel7_value=value; return;
	case ID_CHANNEL7_BEGIN: this->m_channel7_begin=value; return;
	case ID_CHANNEL7_END:   this->m_channel7_end=value; return;
	case ID_CHANNEL8_VALUE: this->m_channel8_value=value; return;
	case ID_CHANNEL8_BEGIN: this->m_channel8_begin=value; return;
	case ID_CHANNEL8_END:   this->m_channel8_end=value; return;
	}
	assert( false && "Should never end up here" );
}

/**
 * Set the instance variable if the corresponding edit box
 *
 * This funtion is made public id MAKE_TEST is defined
 * 
 * @param id Id of the edit box, whose corresponding instance variable is to be get.
 * @return Value of the instance instance variable.
 */
float SweepDialog::getValue( int id )
{
	assertIdIsTextCtrl(id);
	switch( id )
	{
	case ID_WINDX_VALUE: return this->m_wind_x_value;
	case ID_WINDX_BEGIN: return this->m_wind_x_begin; 
	case ID_WINDX_END:   return this->m_wind_x_end; 
	case ID_WINDY_VALUE: return this->m_wind_y_value; 
	case ID_WINDY_BEGIN: return this->m_wind_y_begin; 
	case ID_WINDY_END:   return this->m_wind_y_end; 
	case ID_WINDZ_VALUE: return this->m_wind_z_value; 
	case ID_WINDZ_BEGIN: return this->m_wind_z_begin; 
	case ID_WINDZ_END:   return this->m_wind_z_end; 
	case ID_WINDYAW_VALUE: return this->m_wind_yaw_value; 
	case ID_WINDYAW_BEGIN: return this->m_wind_yaw_begin; 
	case ID_WINDYAW_END:   return this->m_wind_yaw_end; 
	case ID_WINDPITCH_VALUE: return this->m_wind_pitch_value; 
	case ID_WINDPITCH_BEGIN: return this->m_wind_pitch_begin; 
	case ID_WINDPITCH_END:   return this->m_wind_pitch_end; 
	case ID_CHANNEL1_VALUE: return this->m_channel1_value; 
	case ID_CHANNEL1_BEGIN: return this->m_channel1_begin; 
	case ID_CHANNEL1_END:   return this->m_channel1_end; 
	case ID_CHANNEL2_VALUE: return this->m_channel2_value; 
	case ID_CHANNEL2_BEGIN: return this->m_channel2_begin; 
	case ID_CHANNEL2_END:   return this->m_channel2_end; 
	case ID_CHANNEL3_VALUE: return this->m_channel3_value; 
	case ID_CHANNEL3_BEGIN: return this->m_channel3_begin; 
	case ID_CHANNEL3_END:   return this->m_channel3_end; 
	case ID_CHANNEL4_VALUE: return this->m_channel4_value; 
	case ID_CHANNEL4_BEGIN: return this->m_channel4_begin; 
	case ID_CHANNEL4_END:   return this->m_channel4_end; 
	case ID_CHANNEL5_VALUE: return this->m_channel5_value; 
	case ID_CHANNEL5_BEGIN: return this->m_channel5_begin; 
	case ID_CHANNEL5_END:   return this->m_channel5_end; 
	case ID_CHANNEL6_VALUE: return this->m_channel6_value; 
	case ID_CHANNEL6_BEGIN: return this->m_channel6_begin; 
	case ID_CHANNEL6_END:   return this->m_channel6_end; 
	case ID_CHANNEL7_VALUE: return this->m_channel7_value; 
	case ID_CHANNEL7_BEGIN: return this->m_channel7_begin; 
	case ID_CHANNEL7_END:   return this->m_channel7_end; 
	case ID_CHANNEL8_VALUE: return this->m_channel8_value; 
	case ID_CHANNEL8_BEGIN: return this->m_channel8_begin; 
	case ID_CHANNEL8_END:   return this->m_channel8_end; 
	}
	assert( false && "Should never end up here" );
	return 0;
}

/**
 * Interpolate between two values
 *
 * This functions devides the range a to b into steps number of steps, 
 * and returns value the n-th step.
 *
 * @param n Zero-based index of requested step, so 0 <= n < steps.
 * @param steps Number of points in the range
 * @param a First number in the range
 * @param b Last number in the range.
 */
testable_static float interpolate( int n, int steps, float a, float b ) {
	// check intput
	assert( n>=0 );
	assert( n<steps ) ;
	float frac = static_cast<float>(n)/(steps-1);
	last_interpolate =  (1-frac) * a + frac*b;
	return last_interpolate;
}


float SweepDialog::getInterpolatedValue( int idBase, int n, int steps )
{
	assert( n >= 0 );
	assert( n<steps );
	assertIdIsBase( idBase );
	float frac = static_cast<float>(n)/(steps-1);
	return (1-frac) * getValue(ID_BEGIN(idBase)) + frac*getValue(ID_END(idBase));
}

/*
 * Calculate the x part of a given 3D vector first yawed and then pitched.
 *
 * First the 3D vector is rotated around the yaw-axis by the specified angle.
 * Then the vector is rotated arounf te (local) pitch-axis by the specified angle.
 * Note: a right-hand coordinate system is assumed (a positive angle rotates counter clock-wise)
 *
 * @param yaw Angle in radians to rotate around the yaw axis.
 * @param pitch Angle in radians to rotate around the (local) pitch axis.
 * @param x X part of vector to be rotated.
 * @param y Y part of vector to be rotated.
 * @param z Z part of vector to be rotated.
 * @return X part of rotated vector.
 */
testable_static float calcYawPitchX( float yaw, float pitch, float x, float y, float z )
{
	return cos(yaw) * ( cos(pitch) * x + sin(pitch) * z ) - sin(yaw) *y;
}

/*
 * Calculate the y part of a given 3D vector first yawed and then pitched.
 *
 * First the 3D vector is rotated around the yaw-axis by the specified angle.
 * Then the vector is rotated arounf te (local) pitch-axis by the specified angle.
 * Note: a right-hand coordinate system is assumed (a positive angle rotates counter clock-wise)
 *
 * @param yaw Angle in radians to rotate around the yaw axis.
 * @param pitch Angle in radians to rotate around the (local) pitch axis.
 * @param x X part of vector to be rotated.
 * @param y Y part of vector to be rotated.
 * @param z Z part of vector to be rotated.
 * @return Y part of rotated vector.
 */
testable_static float calcYawPitchY( float yaw, float pitch, float x, float y, float z )
{
	return sin(yaw) * ( cos(pitch) * x + sin(pitch) * z ) + cos (yaw) *y;
}

/*
 * Calculate the z part of a given 3D vector first yawed and then pitched.
 *
 * First the 3D vector is rotated around the yaw-axis by the specified angle.
 * Then the vector is rotated arounf te (local) pitch-axis by the specified angle.
 * Note: a right-hand coordinate system is assumed (a positive angle rotates counter clock-wise)
 *
 * @param yaw Angle in radians to rotate around the yaw axis.
 * @param pitch Angle in radians to rotate around the (local) pitch axis.
 * @param x X part of vector to be rotated.
 * @param y Y part of vector to be rotated.
 * @param z Z part of vector to be rotated.
 * @return Z part of rotated vector.
 */
testable_static float calcYawPitchZ( float yaw, float pitch, float x, float y, float z )
{
	return cos(pitch) * z - sin(pitch) * x;
}

/**
 * Fills in interpolated environment and inertias structures.
 *
 * For example: if the dialog has been filled in speciying that channel 3 should
 * be swept from 0.0 to 1.0 using 101 steps, calling this function with step set to 75
 * will fill in the environment and inertias structure with the corresponing VALUE fiels,
 * except for the channel 3 variable in the environment structure, which will be set to 0.75.
 *
 * Note: this function should only be called after the dialog has been shown, filled in, and
 * succeccfully closed by the user using the OK button.
 *
 * @param environment Environment structure to be filled in.
 * @param inertias Inertias structure to be filled in.
 * @param step Number indicating which step to use. Step should be greater or equal to zero,
 * and less than the number of steps, the later is returned by the getNumberOfSteps() function.
 */
bool SweepDialog::get( ModelSimulator_environment_t &environment, ModelSimulator_inertias_t &inertias, int step )
{
	if( this->m_isValidData == false ) return false;

	// TODO: wind rotate
	float wind_x = this->m_chosen_input == ID_WINDX ? interpolate( step, m_steps, m_wind_x_begin, m_wind_x_end ) : m_wind_x_value;
	float wind_y = this->m_chosen_input == ID_WINDY ? interpolate( step, m_steps, m_wind_x_begin, m_wind_x_end ) : m_wind_y_value;
	float wind_z = this->m_chosen_input == ID_WINDZ ? interpolate( step, m_steps, m_wind_x_begin, m_wind_x_end ) : m_wind_z_value;
	float wind_yaw = this->m_chosen_input == ID_WINDYAW ? interpolate( step, m_steps, m_wind_yaw_begin, m_wind_yaw_end ) : m_wind_yaw_value;
	float wind_pitch = this->m_chosen_input == ID_WINDPITCH ? interpolate( step, m_steps, m_wind_pitch_begin, m_wind_pitch_end ) : m_wind_pitch_value;

	environment.linearVelocityX = calcYawPitchX( wind_yaw, wind_pitch, wind_x, wind_y, wind_z );
	environment.linearVelocityY = calcYawPitchY( wind_yaw, wind_pitch, wind_x, wind_y, wind_z );
	environment.linearVelocityZ = calcYawPitchZ( wind_yaw, wind_pitch, wind_x, wind_y, wind_z );
	environment.channel[0] = m_chosen_input == ID_CHANNEL1 ? interpolate( step, m_steps, m_channel1_begin, m_channel1_end ) : m_channel1_value;
	environment.channel[1] = m_chosen_input == ID_CHANNEL2 ? interpolate( step, m_steps, m_channel2_begin, m_channel2_end ) : m_channel2_value;
	environment.channel[2] = m_chosen_input == ID_CHANNEL3 ? interpolate( step, m_steps, m_channel3_begin, m_channel3_end ) : m_channel3_value;
	environment.channel[3] = m_chosen_input == ID_CHANNEL4 ? interpolate( step, m_steps, m_channel4_begin, m_channel4_end ) : m_channel4_value;
	environment.channel[4] = m_chosen_input == ID_CHANNEL5 ? interpolate( step, m_steps, m_channel5_begin, m_channel5_end ) : m_channel5_value;
	environment.channel[5] = m_chosen_input == ID_CHANNEL6 ? interpolate( step, m_steps, m_channel6_begin, m_channel6_end ) : m_channel6_value;
	environment.channel[6] = m_chosen_input == ID_CHANNEL7 ? interpolate( step, m_steps, m_channel7_begin, m_channel7_end ) : m_channel7_value;
	environment.channel[7] = m_chosen_input == ID_CHANNEL8 ? interpolate( step, m_steps, m_channel8_begin, m_channel8_end ) : m_channel8_value;
	for( size_t i=8; i<16; ++i ) environment.channel[i] = 0.f;

	inertias.mass = m_mass;
	inertias.ixx = m_ixx;
	inertias.iyy = m_iyy;
	inertias.izz = m_izz;
	inertias.ixy = m_ixy;
	inertias.ixz = m_ixz;
	inertias.iyz = m_iyz;

	return true; 
}

float SweepDialog::select( int n )
{
	return this->getInterpolatedValue( this->getSelectedInput(), n, this->m_steps);
}

/**
 * Get the chosen output variable from a forces structure
 *
 * For example: if the dialog has been filled in speciying that negative torque 
 * around the y axis should be used as output, a call to this function would return
 * the negated value of the y-torque given in the given forces structure 
 * ( i.e. returns -forces.torque_y ).
 *
 * Note: this function should only be called after the dialog has been shown, filled in, and
 * succeccfully closed by the user using the OK button.
 *
 * @param forces Specifies which forces to choose from
 * @return Returns the chosen force value.
 */
float SweepDialog::select( ModelSimulator_forces_t & forces )
{
	if( this->m_isValidData == false ) return 0.f;
	assertIdIsOutput( m_chosen_output );

	switch( m_chosen_output ) 
	{
	case ID_FORCE_POSX: return forces.force_x;
	case ID_FORCE_POSY: return forces.force_y;
	case ID_FORCE_POSZ: return forces.force_z;
	case ID_FORCE_NEGX: return -forces.force_x;
	case ID_FORCE_NEGY: return -forces.force_y;
	case ID_FORCE_NEGZ: return -forces.force_z;
	case ID_TORQUE_POSX: return forces.torque_x;
	case ID_TORQUE_POSY: return forces.torque_y;
	case ID_TORQUE_POSZ: return forces.torque_z;
	case ID_TORQUE_NEGX: return -forces.torque_x;
	case ID_TORQUE_NEGY: return -forces.torque_y;
	case ID_TORQUE_NEGZ: return -forces.torque_z;
	}
	assert( false && "Should never end up here");
	return 0.f;
}


